package main

import (
	"archive/zip"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"

	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/PuerkitoBio/goquery"
	"github.com/cheggaaa/pb/v3"
	"github.com/go-ini/ini"
)

const Vsersion = "1.0.4"
const cacheFilePath = "ver_cache.json"
const cacheDuration = 10 * 24 * time.Hour // 缓存有效期为10天

type LocalVersion struct {
	Name    string
	Current bool
	Path    string
	Ver     string
}
type VerStruct struct {
	Key   string
	Value string
}

type CachedVersions struct {
	Versions map[string]string
	ExpireAt time.Time // 过期时间
}

// Version 用于表示和比较版本号的结构体
type Version []int

// ParseVersion 将字符串形式的版本号转换为 Version 结构体
func ParseVersion(versionStr string) (Version, error) {
	parts := strings.Split(versionStr, ".")
	version := make(Version, len(parts))
	for i, part := range parts {
		num, err := strconv.Atoi(part)
		if err != nil {
			return nil, err
		}
		version[i] = num
	}
	return version, nil
}

// CompareVersions 比较两个版本号
func CompareVersions(v1, v2 Version, ascending bool) int {
	switch {
	case ascending:
		return compareAscending(v1, v2)
	default:
		return -compareAscending(v1, v2)
	}
}

func compareAscending(v1, v2 Version) int {
	l := len(v1)
	if len(v2) < l {
		l = len(v2)
	}
	for i := 0; i < l; i++ {
		if v1[i] < v2[i] {
			return -1
		} else if v1[i] > v2[i] {
			return 1
		}
	}
	if len(v1) < len(v2) {
		return -1
	} else if len(v1) > len(v2) {
		return 1
	}
	return 0
}

// BySemVer 实现了 sort.Interface 接口，使得切片可以根据版本号进行排序
type BySemVer struct {
	Pairs []VerStruct
	Asc   bool // 设置此标志以决定排序方式，默认为true（升序）
}

func (a BySemVer) Len() int { return len(a.Pairs) }
func (a BySemVer) Swap(i, j int) {
	a.Pairs[i], a.Pairs[j] = a.Pairs[j], a.Pairs[i]
}
func (a BySemVer) Less(i, j int) bool {
	v1, _ := ParseVersion(a.Pairs[i].Key)
	v2, _ := ParseVersion(a.Pairs[j].Key)
	return CompareVersions(v1, v2, a.Asc) < 0
}

func main() {

	defer func() {
		if r := recover(); r != nil {
			// 捕获到了panic，并进行处理
			fmt.Println(r)
		}
	}()

	//	判断os.Args长度
	if len(os.Args) == 1 { // 如果没有参数
		showHelp()
		fmt.Println(" ")
		fmt.Println("请在命令行中使用，按任意键退出")
		buf := make([]byte, 1)
		_, _ = os.Stdin.Read(buf) // 等待用户按回车键

	} else { // 如果有参数
		option := ""
		args := os.Args[1:]
		if len(args) == 2 { // 如果没有参数
			option = args[1]
		}

		switch args[0] {
		case "-h", "help":
			showHelp()
		case "-v", "version":
			fmt.Println("当前运行版本:", Vsersion)
		case "-l", "list":
			for _, v := range getLocalList() {
				biaoji := " "
				if v.Current {
					biaoji = "*"
				}
				fmt.Printf(" %s %s\n", biaoji, v.Name)
			}
		case "-u", "use":
			if !checkVerStr(option) {
				panic("版本号格式错误 版本号示例：8.3或者8.3.1")
			}
			varlist := make(map[string]string, 0)

			for _, v := range getLocalList() {
				varlist[v.Ver] = v.Path
				//sortedPairs = append(sortedPairs, VerStruct{Key: v.Ver, Value: v.Path})
			}
			varlist, _ = findVerListByKey(varlist, option)
			if len(varlist) == 0 {
				panic(fmt.Sprintf("未匹配到版本:%s", option))
			}
			sortedPairs := make([]VerStruct, 0)
			for k, v := range varlist {
				sortedPairs = append(sortedPairs, VerStruct{Key: k, Value: v})
			}

			setDirLink(sortedPairs[0].Value)
			setIniVal("common", "current", filepath.Base(sortedPairs[0].Value))
			fmt.Println("\rPHP版本切换成功")
			//fmt.Println("1.0.0")
		case "-s", "search":
			fmt.Printf("获取版本...")
			varlist, _ := getOnlineVerListByCache()
			fmt.Printf("\r               ")
			fmt.Printf("\r")
			if checkVerStr(option) {
				varlist, _ = findVerListByKey(varlist, option)
			}
			// 将map转换为切片，并按key进行排序
			sortedPairs := make([]VerStruct, 0, len(varlist))
			for k, v := range varlist {
				sortedPairs = append(sortedPairs, VerStruct{Key: k, Value: v})
			}
			if checkOptionFormat(option) {

			} else {
				sortedPairs = mergeVerList(sortedPairs)
			}

			sort.Sort(BySemVer{Pairs: sortedPairs, Asc: false})

			// 打印排序后的版本信息
			for _, pair := range sortedPairs {
				fmt.Println(pair.Key)
			}
		case "-i", "install":
			if !checkVerStr(option) {
				panic("版本号格式错误 版本号示例：8.3或者8.3.1")
			}
			fmt.Printf("获取版本...")
			varlist, _ := getOnlineVerListByCache()
			fmt.Printf("\r               ")
			fmt.Printf("\r")
			varlist, _ = findVerListByKey(varlist, option)
			if len(varlist) == 0 {
				panic(fmt.Sprintf("未找到【%s】相关可安装版本", option))
			}
			// 将map转换为切片，并按key进行排序
			sortedPairs := make([]VerStruct, 0, len(varlist))
			for k, v := range varlist {
				sortedPairs = append(sortedPairs, VerStruct{Key: k, Value: v})
			}

			sort.Sort(BySemVer{Pairs: sortedPairs, Asc: false})

			fmt.Println("可安装版本：", sortedPairs[0].Key)
			url := "https://windows.php.net/downloads/releases/archives/" + sortedPairs[0].Value
			fileName := "php" + sortedPairs[0].Key + ".zip"
			newFile := filepath.Join(getRootDir(), "down/", fileName)
			//判断是否已经下载了文件
			if _, err := os.Stat(newFile); err != nil {
				fmt.Println("下载地址：", url)
				onProgress := func(current, total int64) {
					fmt.Printf("\rDownload Progress: %.2f%% (%d / %d bytes)", float64(current)/float64(total)*100, current, total)
				}
				tmpFile, err := downloadFileWithProgress(url, fileName, onProgress)
				if err != nil {
					fmt.Println(err)
				} else {
					fmt.Println("\nFile downloaded successfully to", tmpFile)

					err := os.Rename(tmpFile, newFile)
					if err != nil {
						panic(fmt.Sprintf("重命名文件失败: %v\n", err))
					}
				}
			}

			err := unzip(newFile, filePath("php/"+sortedPairs[0].Key))
			if err != nil {
				panic(fmt.Sprintf("解压文件失败: %v\n", err))
			}
			phpInit("php/" + sortedPairs[0].Key)

		default:
			showHelp()
		}
	}

}

func init() {
	//生成cmd文件
	_, err := os.Stat(filePath("pvmeval.cmd"))
	if err != nil {
		if os.IsNotExist(err) {
			saveFile("pvmeval.cmd",
				`@setlocal
@echo off

%*
if %ERRORLEVEL% LSS 1 goto :EOF

set CMD=%*
set APP=%1
start wscript //nologo "%~dpn0.vbs" %*`,
			)
		}
	}

	//生成vbs文件
	_, err = os.Stat(filePath("pvmeval.vbs"))
	if err != nil {
		if os.IsNotExist(err) { // 如果错误是“不存在”
			saveFile("pvmeval.vbs",
				`Set Shell = CreateObject("Shell.Application")
Set WShell = WScript.CreateObject("WScript.Shell")
Set ProcEnv = WShell.Environment("PROCESS")

cmd = ProcEnv("CMD")
app = ProcEnv("APP")
args= Right(cmd,(Len(cmd)-Len(app)))

If (WScript.Arguments.Count >= 1) Then
	Shell.ShellExecute app, args, "", "runas", 0
Else
	WScript.Quit
End If`,
			)
		}
	}
	checkIniFile()
	//checkEnv()
}

func showHelp() {
	fmt.Println("Usage: pvm [-h | help] [-v | version] [-l | list] [-u | use <ver> ] [-s | search [-a|all|7.4] ] [-i | install <ver>]")
	fmt.Println("")
	fmt.Println("  ", "   help\t 显示帮助")
	fmt.Println("  ", "version\t 显示版本")
	fmt.Println("  ", " search\t 发行版本列表")
	fmt.Println("  ", "   list\t 本地版本列表")
	fmt.Println("  ", "    use <ver> 设置使用版本")
	fmt.Println("  ", "install <ver> 下载版本")
}
func findVerListByKey(verlist map[string]string, key string) (map[string]string, error) {
	temp := make(map[string]string, 0)
	keys := strings.Split(key, ".")
	for k, v := range verlist {
		tempK := strings.Split(k, ".")
		if tempK[0] != keys[0] {
			continue
		}
		if len(keys) > 1 && len(tempK) > 1 {
			if tempK[1] != keys[1] {
				continue
			}
		}
		if len(keys) > 2 && len(tempK) > 2 {
			if tempK[2] != keys[2] {
				continue
			}
		}
		temp[k] = v
	}
	if len(temp) > 0 {
		return temp, nil
	}
	return nil, errors.New("not found")
}
func checkVerStr(verStr string) bool {
	versionPattern := regexp.MustCompile(`^\d+(\.\d+)(\.\d+)?$`) // 匹配形如 \d 或 \d.\d 的字符串
	// 分别进行匹配
	isVersion := versionPattern.MatchString(verStr)
	// 根据匹配结果返回
	if isVersion {
		return true
	} else {
		return false
	}
}
func checkOptionFormat(option string) bool {
	if len(option) == 0 {
		return false // 如果 option 为空，则返回错误提示或直接返回 false
	}
	if option == "-a" || option == "all" {
		return true
	}
	return checkVerStr(option)
}
func checkEnv() {

	pvmPath := os.Getenv("PVM_HOME")
	if len(pvmPath) == 0 {
		setEnv("PVM_HOME", getRootDir())
	}
	pvmPhpLink := os.Getenv("PVM_PHPLINK")
	if len(pvmPhpLink) == 0 {
		setEnv("PVM_PHPLINK", filepath.Join(getRootDir(), "php_link"))

		path := os.Getenv("PATH")
		newPath := ""
		// 判断path里是否包字符串%PVM_HOME%
		if !strings.Contains(path, "%PVM_HOME%") {
			newPath = "%PVM_HOME%;"
		}
		if !strings.Contains(path, "%PVM_PHPLINK%") {
			newPath = "%PVM_PHPLINK%;" + newPath
		}
		if len(newPath) > 0 {
			setEnv("PATH", newPath+path)
		}
	}

}

func saveFile(fileName, content string) error {
	// 将内容转换为字节切片
	contentBytes := []byte(content)
	err := os.WriteFile(fileName, contentBytes, 0644)
	if err != nil {
		return errors.New("failed to save file: " + err.Error())
	}

	return nil
}

func setEnv(key, val string) {
	_, err := elevatedRun("setx", key, val, "/m")
	if err != nil {
		fmt.Println("设置环境变量失败", err)
		panic(err)
	}
}
func setDirLink(path string) {
	//cmd := exec.Command("cmd", "/c", "mklink", "/D", getRootDir()+"\\php_link", path)
	elevatedRun("rmdir", "/s", "/q", filepath.Join(getRootDir(), "php_link"))
	_, err := elevatedRun("mklink", "/D", filepath.Join(getRootDir(), "php_link"), path)
	if err != nil {
		fmt.Println("设置软连接失败：", err)
		panic(err)
	}
}
func directoryExists(path string) (bool, error) {
	// 使用os.Stat检查路径状态
	fileInfo, err := os.Stat(path)

	if err != nil {
		if os.IsNotExist(err) { // 如果错误是“不存在”
			return false, nil
		}
		// 其他类型的错误（如权限问题）直接返回
		return false, err
	}

	// 当err为nil时，说明路径存在且成功获取了信息
	// 判断是否为目录，因为os.Stat也适用于文件
	return fileInfo.IsDir(), nil
}

func getLocalList() []LocalVersion {
	dirPath := filepath.Join(getRootDir(), "php")
	exists, err := directoryExists(dirPath)
	if err != nil {
		panic(err)
	} else {
		if !exists {
			os.Mkdir(dirPath, os.ModePerm)
			panic("php directory not exists.")
		}
		subDirs, err := GetSubdirectoryNames(dirPath)
		if err != nil {
			panic(err)
		}
		return subDirs
	}
}

// 扫描指定目录下的子目录列表
func GetSubdirectoryNames(dirPth string) ([]LocalVersion, error) {
	dir, err := os.Open(dirPth)
	if err != nil {
		return nil, fmt.Errorf("error opening directory: %w", err)
	}
	defer dir.Close() // Ensure the directory is closed after all operations are done to avoid resource leak.

	entries, err := dir.Readdir(-1) // Read all entries in the directory.
	if err != nil {
		return nil, fmt.Errorf("error reading directory: %w", err)
	}

	re := regexp.MustCompile(`.*(\d+\.\d+\.\d+).*`)

	rootDir := getRootDir()
	current := getCurrentVersion()
	subDirs := make([]LocalVersion, 0) // No need to pre-allocate as the number of subdirectories isn't known in advance.
	for _, entry := range entries {
		if entry.IsDir() {
			matches := re.FindStringSubmatch(entry.Name())
			if len(matches) > 1 {
				subDirs = append(subDirs,
					LocalVersion{
						Name:    entry.Name(),
						Path:    filepath.Join(rootDir, "php", entry.Name()),
						Current: current == entry.Name(),
						Ver:     matches[1],
					},
				)
			}

		}
	}
	return subDirs, nil
}

func getCurrentVersion() string {
	current, _ := getIniVal("common", "current")
	return current
}
func checkIniFile() error {
	if _, err := os.Stat(filePath("pvm.ini")); os.IsNotExist(err) {
		cfg := ini.Empty()
		cfg.SaveTo(filePath("pvm.ini"))
		checkEnv() //根据pvm.ini判断 执行一次
	}
	return nil
}

// getIniVal 从指定的INI文件中获取指定节(section)和键(key)的值
func getIniVal(section, key string) (string, error) {
	// 检查INI文件
	err := checkIniFile()
	if err != nil {
		return "", err // 返回错误给调用者
	}

	// 加载INI文件
	cfg, err := ini.Load(filePath("pvm.ini"))
	if err != nil {
		return "", fmt.Errorf("Failed to load INI file: %s", err)
	}

	// 获取键值
	value := cfg.Section(section).Key(key).Value()

	return value, nil
}

func setIniVal(section, key, val string) error {
	// 检查INI文件
	err := checkIniFile()
	if err != nil {
		return err // 返回错误给调用者
	}

	// 加载INI文件
	cfg, err := ini.Load(filePath("pvm.ini"))
	if err != nil {
		return fmt.Errorf("Failed to load INI file: %s", err)
	}

	// 获取键值
	cfg.Section(section).Key(key).SetValue(val)
	// 保存到文件
	err = cfg.SaveTo(filePath("pvm.ini"))
	if err != nil {
		return nil
	}
	return nil
}
func getRootDir() string {
	execPath, err := os.Executable()
	if err != nil {
		panic(err)
	}
	return filepath.Dir(execPath)
}

func elevatedRun(name string, arg ...string) (bool, error) {
	ok, err := run("cmd", append([]string{"/C", name}, arg...)...)
	if err != nil {
		ok, err = run(filepath.Join(getRootDir(), "pvmeval.cmd"), append([]string{"cmd", "/C", name}, arg...)...)
	}
	return ok, err
}
func run(name string, arg ...string) (bool, error) {
	c := exec.Command(name, arg...)
	var stderr bytes.Buffer
	c.Stderr = &stderr
	err := c.Run()
	if err != nil {
		return false, errors.New(fmt.Sprint(err) + ": " + stderr.String())
	}
	return true, nil
}
func getOnlineVerListByCache() (map[string]string, error) {
	// 加载缓存
	cachedVersions, err := loadCachedVersions(cacheFilePath)
	if err != nil {
		return getOnlineVerList()
	}

	// 检查缓存是否已过期
	if time.Now().Before(cachedVersions.ExpireAt) {
		return cachedVersions.Versions, nil
	}
	//缓存不存在或已过期，重新获取线上版本列表并更新缓存
	return getOnlineVerList()
}

// 加载缓存版本信息
func loadCachedVersions(cacheFile string) (*CachedVersions, error) {
	content, err := os.ReadFile(filePath(cacheFile))
	if err != nil {
		return nil, err
	}

	var cached CachedVersions
	err = json.Unmarshal(content, &cached)
	if err != nil {
		return nil, err
	}

	return &cached, nil
}

// 更新缓存
func updateCache(versions map[string]string) error {
	cachedVersions := &CachedVersions{
		Versions: versions,
		ExpireAt: time.Now().Add(cacheDuration),
	}

	// 序列化并保存到文件
	content, err := json.Marshal(cachedVersions)
	if err != nil {
		return err
	}

	err = os.WriteFile(filePath(cacheFilePath), content, 0644)
	if err != nil {
		return err
	}

	return nil
}

func getOnlineVerList() (map[string]string, error) {
	// 目标URL
	url := "https://windows.php.net/downloads/releases/archives/"

	// 发送GET请求获取网页内容
	resp, err := http.Get(url)
	if err != nil {
		log.Fatal("Error fetching URL:", err)
	}
	defer resp.Body.Close()

	// 用goquery加载HTTP响应体
	doc, err := goquery.NewDocumentFromReader(resp.Body)
	if err != nil {
		log.Fatal("Error parsing HTML:", err)
	}

	// 正则表达式用于匹配以"php-"开头的字符串
	re := regexp.MustCompile(`^php-\d+\.\d+\.\d+-nts.+-x64.zip$`)
	m := make(map[string]string)
	// 查找所有匹配PHP x64 zip文件的a标签
	doc.Find("a[href*=php-]").Each(func(i int, s *goquery.Selection) {
		linkText := s.Text()
		//href, exists := s.Attr("href")
		//if exists && re.MatchString(linkText) {
		if re.MatchString(linkText) {
			ver, _ := extractVersionFromLink(linkText)
			if ver != "" {
				m[ver] = linkText
			}
		}
	})

	updateCache(m)
	return m, nil
}

func extractVersionFromLink(link string) (string, error) {
	re := regexp.MustCompile(`php-(\d+\.\d+\.\d+).*\.zip`)
	matches := re.FindStringSubmatch(link)
	if len(matches) > 1 {
		return matches[1], nil
	}
	return "", fmt.Errorf("No version found in the link")
}

func mergeVerList(list []VerStruct) []VerStruct {

	tmpList := make(map[string]map[string]interface{}, 0)

	for _, val := range list {
		parts := strings.Split(val.Key, ".")
		key := strings.Join(parts[:2], "-")

		num, _ := strconv.Atoi(parts[2])

		if storedVal, ok := tmpList[key]; ok {
			currentNum, ok := storedVal["num"].(int)
			if ok && currentNum < num {
				tmpList[key]["num"] = num
				tmpList[key]["VerStruct"] = val
			}
		} else {
			tmpList[key] = map[string]interface{}{
				"num":       num,
				"VerStruct": val,
			}
		}
	}
	tmp := make([]VerStruct, 0)

	for _, v := range tmpList {
		tmp = append(tmp, v["VerStruct"].(VerStruct))
	}

	return tmp

}

// downloadFileWithProgress 下载指定URL的文件到目标路径，并通过回调函数显示下载进度。
func downloadFileWithProgress(url, dst string, onProgress func(int64, int64)) (string, error) {
	timestamp := time.Now().Unix()
	tempFileName := fmt.Sprintf("%s_%d.down", dst, timestamp)
	resp, err := http.Get(url)
	if err != nil {
		return "", fmt.Errorf("Error while trying to download: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("Received non-200 status code: %d", resp.StatusCode)
	}

	// 获取完整路径
	filePath := filePath("down/" + tempFileName)

	// 检查目录是否存在，如果不存在则尝试创建
	dir := filepath.Dir(filePath)
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		err = os.MkdirAll(dir, 0755)
		if err != nil {
			panic(fmt.Sprintf("Failed to create directory: %v\n", err))
		}
	}
	file, err := os.Create(filePath)
	if err != nil {
		return "", fmt.Errorf("Error creating file: %w", err)
	}
	defer file.Close()

	contentLength := resp.ContentLength
	if contentLength <= 0 {
		io.Copy(file, resp.Body)
		return "", nil
	}

	bar := pb.New(int(contentLength))
	//bar.SetUnits(pb.U_BYTES)
	bar.Start()
	reader := bar.NewProxyReader(resp.Body)

	_, err = io.Copy(file, reader)
	if err != nil {
		return "", fmt.Errorf("Error writing to file: %w", err)
	}

	bar.Finish()

	onProgress(contentLength, contentLength) // 调用回调函数通知下载完成

	return filePath, nil
}

// unzip 解压缩 zipFilePath 指定的 ZIP 文件到 targetDir 目录下。
// 如果目标文件已存在，将会被覆盖。
func unzip(zipFilePath string, targetDir string) error {
	// 打开 ZIP 文件
	zipFile, err := zip.OpenReader(zipFilePath)
	if err != nil {
		return fmt.Errorf("无法打开 ZIP 文件: %w", err)
	}
	defer zipFile.Close()

	//判断targetDir文件夹是否存在
	if _, err := os.Stat(filepath.Join(getRootDir(), targetDir)); os.IsNotExist(err) {
		err = os.MkdirAll(filepath.Join(getRootDir(), targetDir), 0755)
		if err != nil {
			return fmt.Errorf("无法创建目录 %s: %w", targetDir, err)
		}
	}

	for _, zipEntry := range zipFile.Reader.File {
		entryFilePath := filepath.Join(targetDir, zipEntry.Name)

		// 需要判断目录是否存在
		if !strings.HasSuffix(entryFilePath, "/") {
			// 判断父目录是否存在
			parentDir := filepath.Dir(entryFilePath)
			if _, err := os.Stat(parentDir); os.IsNotExist(err) {
				err = os.MkdirAll(parentDir, 0755)
				if err != nil {
					return fmt.Errorf("无法创建目录 %s: %w", parentDir, err)
				}
			}

		}

		if zipEntry.FileInfo().IsDir() {
			err := os.MkdirAll(filepath.Dir(entryFilePath), zipEntry.Mode())
			if err != nil {
				return fmt.Errorf("无法创建目录 %s: %w", entryFilePath, err)
			}
			continue
		}

		// 注意：os.Create() 会覆盖已存在的文件
		targetFile, err := os.Create(entryFilePath)
		if err != nil {
			return fmt.Errorf("无法创建或覆盖目标文件 %s: %w", entryFilePath, err)
		}
		defer targetFile.Close()

		fileReader, err := zipEntry.Open()
		if err != nil {
			return fmt.Errorf("无法打开 ZIP 中的文件 %s: %w", zipEntry.Name, err)
		}
		defer fileReader.Close()

		_, err = io.Copy(targetFile, fileReader)
		if err != nil {
			return fmt.Errorf("无法写入目标文件 %s: %w", entryFilePath, err)
		}
	}

	fmt.Println("\r" + fmt.Sprintf("文件已成功解压到 %s\n", targetDir))
	return nil
}

func phpInit(phpDir string) {
	oldFile := filepath.Join(getRootDir(), phpDir, "php.ini-development")
	newFile := filepath.Join(getRootDir(), phpDir, "php.ini")

	contentBytes, err := os.ReadFile(oldFile)
	if err != nil {
		fmt.Println("Error replacing texts in file:", err)
	}
	contentStr := string(contentBytes)
	contentStr = strReplace(contentStr, `;\s*extension_dir\s*=\s*"ext"`, `extension_dir="./ext"`)
	contentStr = strReplace(contentStr, `;\s*extension\s*=\s*php_curl.dll`, "extension=php_curl.dll")
	contentStr = strReplace(contentStr, `;\s*extension\s*=\s*gd`, "extension=gd")
	contentStr = strReplace(contentStr, `;\s*extension\s*=\s*curl`, "extension=curl")
	contentStr = strReplace(contentStr, `;\s*extension\s*=\s*gd2`, "extension=gd2")
	contentStr = strReplace(contentStr, `;\s*extension\s*=\s*php_gd2.dll`, "extension=php_gd2.dll")
	contentStr = strReplace(contentStr, `;\s*extension\s*=\s*mbstring`, "extension=mbstring")
	contentStr = strReplace(contentStr, `;\s*extension\s*=\s*php_mbstring.dll`, "extension=php_mbstring.dll")
	contentStr = strReplace(contentStr, `;\s*extension\s*=\s*mysqli`, "extension=mysqli")
	contentStr = strReplace(contentStr, `;\s*extension\s*=\s*php_mysqli.dll`, "extension=php_mysqli.dll")
	contentStr = strReplace(contentStr, `;\s*extension\s*=\s*openssl`, "extension=openssl")
	contentStr = strReplace(contentStr, `;\s*extension\s*=\s*php_openssl.dll`, "extension=php_openssl.dll")
	contentStr = strReplace(contentStr, `;\s*extension\s*=\s*pdo_mysql`, "extension=pdo_mysql")
	contentStr = strReplace(contentStr, `;\s*extension\s*=\s*php_pdo_mysql.dll`, "extension=php_pdo_mysql.dll")

	// 写回文件
	err = os.WriteFile(newFile, []byte(contentStr), 0644)
	if err != nil {
		fmt.Println("Error replacing texts in file:", err)
	}
	// replacements := map[string]string{
	// 	";extension=php_curl.dll":      "extension=php_curl.dll",
	// 	";extension=php_gd2.dll":       "extension=php_gd2.dll",
	// 	";extension=php_mbstring.dll":  "extension=php_mbstring.dll",
	// 	";extension=php_pdo_mysql.dll": "extension=php_pdo_mysql.dll",
	// 	";extension=php_openssl.dll":   "extension=php_pdo_mysql.dll",
	// 	// 添加更多替换对...
	// }

}

// replaceTextsInFile 用于在文件中替换多个文本片段。
func replaceTextsInFile(filePath string, newfilePath string, replacements map[string]string) error {
	// 读取文件内容
	contentBytes, err := os.ReadFile(filePath)
	if err != nil {
		return err
	}
	contentStr := string(contentBytes)

	// 遍历替换项并进行替换
	for oldText, newText := range replacements {
		contentStr = strings.ReplaceAll(contentStr, oldText, newText)
	}

	// 写回文件
	err = os.WriteFile(newfilePath, []byte(contentStr), 0644)
	if err != nil {
		return err
	}

	return nil
}

func filePath(fileName string) string {
	return filepath.Join(getRootDir(), fileName)
}

func strReplace(str string, search string, replace string) string {
	re := regexp.MustCompile(search)
	res := re.ReplaceAllString(str, replace)
	return res
}
